package com.bosssoft.boss_exam_core.common.aspect;

import com.bosssoft.boss_exam_core.common.config.security.AdminJwtTokenHelper;
import com.bosssoft.boss_exam_core.handler.GlobalExceptionHandler;
import com.bosssoft.boss_exam_core.util.web.IpUtil;
import com.bosssoft.boss_exam_db.entity.Admin;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Optional;


/***
 * @Author lys(cherish)
 * @Phone 17720799636
 * @Date  2019/3/5
 * @Description 请求参数、响应体统一日志打印
 * @Exception
 * @since JDK 1.8
 **/
@Slf4j
@Component
@Aspect
public class RestControllerAspect {

	/***
	 * @Author lys(cherish)
	 * @Phone 17720799636
	 * @Date  2019/3/5
	 * @Description 环绕通知
	 * @Param [joinPoint] 连接点
	 * @Return java.lang.Object 切入点返回值
	 * @Exception 异常信息
	 * @since JDK 1.8
	 **/
	@Around("@within(org.springframework.web.bind.annotation.RestController) || @annotation(org.springframework.web.bind.annotation.RestController)")
	public Object apiLog(ProceedingJoinPoint joinPoint) throws Throwable {
		if(joinPoint==null){
			return null;
		}
//		获取正在执行的方法并判断是否需要打印日志
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		boolean logFlag = this.needToLog(method);
		if (!logFlag) {
			return joinPoint.proceed();
		}
//		获取HttpServletRequest 对象
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//		获取正在登入系统的用户
        Optional<Admin> currentLoginUser = new AdminJwtTokenHelper().getCurrentLoginUser();
//		请求发起所在ip地址
		String ip = IpUtil.getRealIp(request);
//		请求方法名
		String methodName = this.getMethodName(joinPoint);
//		请求参数
        String params = this.getParamsJson(joinPoint);
        String requester=null;
		if(currentLoginUser!=null&&currentLoginUser.isPresent()){
		    requester = String.valueOf(currentLoginUser.get().getUsername());
        }else {
            requester =  "unknown ";
        }
//		用户代理
		String userAgent = request.getHeader("user-agent");
		log.info("Started request requester【用户请求】 [{}] method 【请求方法】 [{}] params 【请求参数】 [{}] IP 源IP地址】 [{}]  userAgent 【用户代理】 [{}]", requester, methodName, params, ip,  userAgent);
		long start = System.currentTimeMillis();
		Object result = joinPoint.proceed();
		return result;
	}
	/***
	 * @Author lys(cherish)
	 * @Phone 17720799636
	 * @Date  2019/3/5
	 * @Description  获取执行方法名称
	 * @Param [joinPoint] 连接点
	 * @Return java.lang.String 方法名称
	 * @Exception
	 * @since JDK 1.8
	 **/
	private String getMethodName(ProceedingJoinPoint joinPoint) {
		String methodName = joinPoint.getSignature().toShortString();
		String shortMethodNameSuffix = "(..)";
		if (methodName.endsWith(shortMethodNameSuffix)) {
			methodName = methodName.substring(0, methodName.length() - shortMethodNameSuffix.length());
		}
		return methodName;
	}

	private String getParamsJson(ProceedingJoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();
		StringBuilder sb = new StringBuilder();
		for (Object arg : args) {
			//移除敏感内容
			String paramStr="";
			if (arg instanceof HttpServletResponse) {
				paramStr = HttpServletResponse.class.getSimpleName();
			} else if (arg instanceof HttpServletRequest) {
				paramStr = HttpServletRequest.class.getSimpleName();
			} else if (arg instanceof MultipartFile) {
				long size = ((MultipartFile) arg).getSize();
				paramStr = MultipartFile.class.getSimpleName() + " size:" + size;
			}
			sb.append(paramStr).append(",");
		}
		if(sb.toString().length()==0){
			return sb.toString();
		}
		return sb.deleteCharAt(sb.length() - 1).toString();
	}

	private boolean needToLog(Method method) {
		//GET请求不记录日志
		return method.getAnnotation(GetMapping.class) == null
				&& !method.getDeclaringClass().equals(GlobalExceptionHandler.class);
	}


}
